";
art_manager_send_email($curator->email, $subject, $body);
$dispatch_count++;
}
if ($dispatch_count > 0) {
$wpdb->query($wpdb->prepare("UPDATE {$wpdb->prefix}art_campaigns SET target_curators = target_curators + %d WHERE id = %d", $dispatch_count, $b_id));
echo '
β‘ Campaign successfully converted to Open Bounty AND blasted to ' . $dispatch_count . ' active curators!
';
} else {
echo '
β‘ Campaign updated to Open Bounty. (All active curators were already pitched).
';
}
}
// RESEND BOUNTY EMAILS HANDLER (GROUPED BY EMAIL SO 1 PER CURATOR)
if (isset($_POST['art_resend_bounty_emails']) && isset($_POST['camp_id_resend_bounty'])) {
$resend_id = intval($_POST['camp_id_resend_bounty']);
$camp = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}art_campaigns WHERE id = %d", $resend_id));
if ($camp) {
$pending_pitches = $wpdb->get_results($wpdb->prepare("
SELECT MAX(p.id) as pitch_id, cur.name, cur.email
FROM {$wpdb->prefix}art_pitches p
JOIN {$wpdb->prefix}art_curators cur ON p.curator_id = cur.id
WHERE p.campaign_id = %d AND p.status = 'pending'
GROUP BY cur.email
", $resend_id));
$resend_count = 0;
$dash_url = home_url('/curator-dashboard/');
foreach ($pending_pitches as $pp) {
$subject = "π¨ OPEN BOUNTY REMINDER: Claim Your Spot!";
$body = "
";
$body .= "
Hey " . esc_html($pp->name) . ",
";
$body .= "
This is a quick reminder that an Open Bounty track is active in your queue!
";
$body .= "
Please Note: This is a network-wide open bounty, not a direct targeted pitch. The song may not perfectly fit your playlist's specific vibe.
";
$body .= "
";
$body .= "
First Come, First Serve: Limited budget pool remaining. Spots close automatically once the budget depletes.
";
$body .= "
Select Your Playlist: If you accept the track, you can choose which of your approved playlists you placed it on directly inside the dashboard.
";
$body .= "
Payment Terms: You ONLY get paid if you ACCEPT and add the song. You do not get paid for declining.
";
$body .= "
No Penalties: Zero negative impact on your Trust Score (XP) if you skip or decline it.
';
}
$active_campaigns = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}art_campaigns WHERE status = 'active' ORDER BY id DESC");
$completed_campaigns = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}art_campaigns WHERE status = 'completed' ORDER BY id DESC LIMIT 50");
echo '
π Deploy New Tracking Campaign
';
echo '
π’ Active Running System Campaigns
';
echo '
Order Ref
Song Stream Details
Target Mappings
Status & Bounty Budget
Actions
';
if(empty($active_campaigns)) { echo '
No track distribution campaigns are running currently.
'; }
else {
foreach($active_campaigns as $camp) {
// Check Unread Admin Reviews
$unread = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->prefix}art_pitches WHERE campaign_id = %d AND status IN ('accepted', 'declined') AND admin_seen = 0", $camp->id));
$unread_badge = $unread > 0 ? ''.$unread.' New Update' : '';
$bounty_info = "";
if (intval($camp->on_bounty_board) === 1) {
$spent = floatval($wpdb->get_var($wpdb->prepare("SELECT SUM(earned_fee) FROM {$wpdb->prefix}art_pitches WHERE campaign_id = %d AND status = 'accepted'", $camp->id)));
$orig_budget = floatval($camp->bounty_budget);
$rem_budget = max(0, $orig_budget - $spent);
$bounty_info = ' β‘ BOUNTY: Spent $' . number_format($spent,2) . ' / $' . number_format($orig_budget,2) . ' π° Remaining Pool: $' . number_format($rem_budget,2) . '';
}
echo '
';
echo '';
}
}
// ==========================================
// 9. INDIVIDUAL CAMPAIGN AUDIT BREAKDOWN VIEW & EXTENSION
// ==========================================
if (!function_exists('art_manager_render_campaign_details')) {
function art_manager_render_campaign_details($campaign_id) {
global $wpdb;
$c = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}art_campaigns WHERE id = %d", $campaign_id));
if (!$c) { echo '
Campaign record missing.
'; return; }
$taxonomy = art_get_admin_master_taxonomies();
// Clear Unread Admin Flag now that we are looking at it
$wpdb->query($wpdb->prepare("UPDATE {$wpdb->prefix}art_pitches SET admin_seen = 1 WHERE campaign_id = %d AND status IN ('accepted', 'declined') AND admin_seen = 0", $campaign_id));
if (isset($_POST['art_add_curators_existing'])) {
$selected_curators = isset($_POST['selected_curators']) ? $_POST['selected_curators'] : array();
$dispatch_count = 0; $dash_url = home_url('/curator-dashboard/');
// Get already pitched curator IDs for this specific campaign to prevent duplicates
$already_pitched = $wpdb->get_col($wpdb->prepare("SELECT curator_id FROM {$wpdb->prefix}art_pitches WHERE campaign_id = %d", $campaign_id));
foreach ($selected_curators as $cid) {
$curator_id_int = intval($cid);
// SKIP IF CURATOR WAS ALREADY CONTACTED FOR THIS CAMPAIGN
if (in_array($curator_id_int, $already_pitched)) {
continue;
}
$curator = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}art_curators WHERE id = %d", $curator_id_int));
if (!$curator) continue;
$wpdb->insert($wpdb->prefix . 'art_pitches', array(
'campaign_id' => $campaign_id, 'curator_id' => $curator->id, 'status' => 'pending',
'payout_status' => 'unpaid', 'curator_token' => wp_generate_password(32, false), 'sent_at' => current_time('mysql')
));
if (intval($c->on_bounty_board) === 1 && floatval($c->bounty_budget) > 0) {
$subject = "π¨ OPEN BOUNTY: Claim Your Spot!";
$body = "
";
$body .= "
Hey " . esc_html($curator->name) . ",
";
$body .= "
An exciting Open Bounty has just been deployed to the network!
";
$body .= "
Please Note: This is a network-wide open bounty, not a direct targeted pitch. The song may not perfectly fit your playlist's specific vibe.
";
$body .= "
";
$body .= "
First Come, First Serve: Limited budget pool. Spots close automatically once the budget depletes.
";
$body .= "
Select Your Playlist: If you accept the track, you can choose which of your approved playlists you placed it on directly inside the dashboard.
";
$body .= "
Payment Terms: You ONLY get paid if you ACCEPT and add the song. You do not get paid for declining.
";
$body .= "
No Penalties: Zero negative impact on your Trust Score (XP) if you skip or decline it.
";
art_manager_send_email($curator->email, $subject, $body);
$dispatch_count++;
}
if ($dispatch_count > 0) {
$wpdb->query($wpdb->prepare("UPDATE {$wpdb->prefix}art_campaigns SET target_curators = target_curators + %d WHERE id = %d", $dispatch_count, $campaign_id));
$c = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}art_campaigns WHERE id = %d", $campaign_id));
echo '
β Open Bounty successfully blasted to ' . $dispatch_count . ' active curators!
';
} else {
echo '
Notice: All active curators have already been contacted for this campaign.
';
}
}
// RESEND BOUNTY EMAILS HANDLER (GROUPED BY EMAIL SO 1 PER CURATOR)
if (isset($_POST['art_resend_bounty_emails']) && isset($_POST['camp_id_resend_bounty'])) {
$resend_id = intval($_POST['camp_id_resend_bounty']);
$camp = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}art_campaigns WHERE id = %d", $resend_id));
if ($camp) {
$pending_pitches = $wpdb->get_results($wpdb->prepare("
SELECT MAX(p.id) as pitch_id, cur.name, cur.email
FROM {$wpdb->prefix}art_pitches p
JOIN {$wpdb->prefix}art_curators cur ON p.curator_id = cur.id
WHERE p.campaign_id = %d AND p.status = 'pending'
GROUP BY cur.email
", $resend_id));
$resend_count = 0;
$dash_url = home_url('/curator-dashboard/');
foreach ($pending_pitches as $pp) {
$subject = "π¨ OPEN BOUNTY REMINDER: Claim Your Spot!";
$body = "
";
$body .= "
Hey " . esc_html($pp->name) . ",
";
$body .= "
This is a quick reminder that an Open Bounty track is active in your queue!
";
$body .= "
Please Note: This is a network-wide open bounty, not a direct targeted pitch. The song may not perfectly fit your playlist's specific vibe.
";
$body .= "
";
$body .= "
First Come, First Serve: Limited budget pool remaining. Spots close automatically once the budget depletes.
";
$body .= "
Select Your Playlist: If you accept the track, you can choose which of your approved playlists you placed it on directly inside the dashboard.
";
$body .= "
Payment Terms: You ONLY get paid if you ACCEPT and add the song. You do not get paid for declining.
";
$body .= "
No Penalties: Zero negative impact on your Trust Score (XP) if you skip or decline it.
β Successfully re-dispatched Open Bounty emails to ' . $resend_count . ' pending curators!
';
}
}
// LEFT JOIN GUARANTEES 100% OF PITCHED CURATORS SHOW UP IN THE MATRIX
$pitches = $wpdb->get_results($wpdb->prepare("
SELECT p.*, cur.email, cur.playlist_name, cur.playlist_link, cur.name as curator_name
FROM {$wpdb->prefix}art_pitches p
LEFT JOIN {$wpdb->prefix}art_curators cur ON p.curator_id = cur.id
WHERE p.campaign_id = %d
ORDER BY FIELD(p.status, 'accepted', 'declined', 'pending', 'sent'), p.id DESC
", $campaign_id));
$accepted_count = 0;
foreach ($pitches as $check_p) { if ($check_p->status === 'accepted') $accepted_count++; }
echo '
This tool automatically compiles all Accepted reviews into beautiful email HTML. If you wish to include specific declined reviews, tick their checkbox in the table above.